![[Swift] AlamofireでURLクエリパラメータに配列を渡す時、ブラケットが付かないようにしたい](https://devio2023-media.developers.io/wp-content/uploads/2018/12/eyecatch-swift.png)
[Swift] AlamofireでURLクエリパラメータに配列を渡す時、ブラケットが付かないようにしたい
はじめに
CX事業本部の中安です。まいどです。
ネットワーク通信ライブラリの代表格であるAlamofire
を使用したアプリを開発しているのですが、
とあるGETで取得するAPIに通信しようとする際に、URLクエリパラメータの仕様で少し悩ましい部分がありました。
その仕様とは「ひとつのキーに複数の値を渡すときに、その個数分だけそのキーを使う」というものです。
文章だとわかりづらいので、例を出してみます。
https://(APIのエンドポイント)?loc=tokyo&loc=osaka&loc=nagoya&date=20230509
この例では、loc
というキーに対して tokyo
osaka
nagoya
という複数の値を渡そうとしています。
APIの仕様としてスタンダードなのかどうかは分かりませんが、こういう仕様なのでそれに合わせないといけません。
このうえで、Alamofire
に対してはEncodable
なリクエストパラメータオブジェクトを渡すことになっています。
それは、次項で例示したいと思います。
元々のソースコード
こちらが実際に開発していたアプリに近しい作りのソースコードです。
リクエストパラメータ用の構造体を定義し、それをインスタンス化して、Alamofire
のセッション(AF)に渡してリクエストしています。
// リクエストパラメータの定義 struct ExampleAPIRequestParameters: Encodable { let date: String let location: String enum CodingKeys: String, CodingKey { case date case location = "loc" } } // リクエストパラメータオブジェクトの作成 let params = ExampleAPIRequestParameters( date: "20230509", location: "tokyo" ) // Alamofireを使用したリクエスト実行 let url = "http://(APIのエンドポイント)" AF.request(url, method: .get, parameters: params).responseData { response in // 何か処理 }
しかし、上記のソースコードではloc
は複数の値を渡せません。
文字列配列に変える
loc
に複数の値を渡すには、もちろん文字列型を文字列配列型に変えるわけです。
struct ExampleAPIRequestParameters: Encodable { let date: String let locations: [String] enum CodingKeys: String, CodingKey { case date case locations = "loc" } }
let params = ExampleAPIRequestParameters( date: "20230509", locations: ["tokyo", "osaka", "nagoya"] )
これで解決と思いきや、残念ながらAlamofire
で変換されるURLは以下のようになります。
https://(APIのエンドポイント)?loc[]=tokyo&loc[]=osaka&loc[]=nagoya&date=20230509
URLクエリパラメータで配列であることを示すブラケット([]
)がついてしまうのです。
URLクエリパラメータの仕様としては間違っていませんが、APIの仕様には合わないものとなってしまいました。
ブラケットを取り払う
このブラケットが付いてしまう原因は、Alamofire
が用意するパラメータエンコーダが関与しています。
AF.request()
をする際に、パラメータエンコーダは指定しない限りはURLEncodedFormParameterEncoder.default
という定義のエンコーダを使用し、その中身は「配列にはブラケットを付けよ」という設定がなされているのでした。
なので、ブラケットを取り払うにはデフォルトのものは使用せず、自分でエンコーダを作ってあげる必要があります。
// 「配列にはブラケットを付けない」という設定のエンコーダを作成する let arrayToNoBracketsURLEncoder = URLEncodedFormEncoder(arrayEncoding: .noBrackets) // 上記のものをエンコーダに設定したパラメータエンコーダを作成する let parameterEncoder = URLEncodedFormParameterEncoder(encoder: arrayToNoBracketsURLEncoder) // リクエスト時に上記のパラメータエンコーダを渡して実行する let url = "http://(APIのエンドポイント)" AF.request(url, method: .get, parameters: params, encoder: parameterEncoder).responseData { response in // 何か処理 }
こうすることにより、求めていたパラメータの形式でリクエストがされるようになりました。
https://(APIのエンドポイント)?loc=tokyo&loc=osaka&loc=nagoya&date=20230509
このように、ちょっと変わった仕様のAPIに対してもクエリパラメータのフォーマットを変更することができるようになります。
指定できる配列のフォーマット
ここでは ArrayEncoding
を.noBrackets
を指定しましたが、他にはどんな設定ができるかというと
- .brackets: ブラケットをつける
loc[]=tokyo&loc[]=osaka&loc[]=nagoya
- .noBrackets: ブラケットをつけない
loc=tokyo&loc=osaka&loc=nagoya
- .indexInBrackets: ブラケットに添字をつける
loc[0]=tokyo&loc[1]=osaka&loc[2]=nagoya
その他指定できる設定
ここまで紹介した配列のフォーマットの他にも、URLEncodedFormEncoder
を作る際には以下のような設定を施すことが可能です。
- alphabetizeKeyValuePairs: キーをアルファベット順に並べるかどうか
- arrayEncoding: 配列のフォーマット (ここまで述べた通り)
- boolEncoding: Bool型を
true/false
1/0
で記述するかを設定できる - dataEncoding: バイナリデータをBase64や他の形式でクエリ化できる
- dateEncoding: 日付型をどのような形式で文字列化するかを設定できる
- keyEncoding: キー名をキャメルケースやスネークケースに変えることができる
- spaceEncoding: 空白スペースを「+」にしたり、エスケープ文字にすることができる
- allowedCharacters: 使用できる文字を制御できる
終わりに
Alamofire
でURLクエリパラメータを指定する際には、それなりに柔軟なフォーマットや設定に対応してくれていることがわかりました。
もし、同じようなところで躓いた方の何かの参考になれば幸いです。
では、またー